package com.zzh.lib.core.utils;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;

import com.zzh.lib.core.HLibrary;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;

/**
 * Created by ZZH on 4/7/21.
 *
 * @Date: 4/7/21
 * @Email: zzh_hz@126.com
 * @QQ: 1299234582
 * @Author: zzh
 * @Description: 关闭操作
 */
public class HIOUtils {
    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
    public static final int EOF = -1;

    private HIOUtils() {
    }

    /**
     * 安全关闭
     *
     * @param closeable
     */
    public static void closeQuietly(Closeable closeable) {
        try {
            if (closeable != null) {
                closeable.close();
                closeable = null;
            }
        } catch (IOException ioe) {
            // ignore
        }
    }


    public static int copy(final InputStream input, final OutputStream output) throws IOException {
        final long count = copyLarge(input, output);
        if (count > Integer.MAX_VALUE) {
            return -1;
        }
        return (int) count;
    }

    public static void copy(File srcFile, File destFile, boolean append) throws IOException {
        long start = System.currentTimeMillis();
        if (srcFile == null || !srcFile.exists()) {
            return;
        }

        FileInputStream fileIns = null;
        FileOutputStream fileOuts = null;

        FileChannel source = null;
        FileChannel destination = null;

        try {
            fileIns = new FileInputStream(srcFile);
            fileOuts = openOutputStream(destFile, false);
            source = fileIns.getChannel();
            destination = fileOuts.getChannel();
            if (append) {
                destination.transferFrom(source, srcFile.length(), source.size());
            } else {
                destination.transferFrom(source, 0, source.size());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeQuietly(fileIns);
            closeQuietly(fileOuts);
            closeQuietly(source);
            closeQuietly(destination);
        }

    }

    public static long copyLarge(final InputStream input, final OutputStream output)
            throws IOException {
        return copy(input, output, DEFAULT_BUFFER_SIZE);
    }

    public static long copy(final InputStream input, final OutputStream output, final int bufferSize)
            throws IOException {
        return copyLarge(input, output, new byte[bufferSize]);
    }

    public static long copyLarge(final InputStream input, final OutputStream output, final byte[] buffer)
            throws IOException {
        long count = 0;
        int n;
        while (EOF != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
            count += n;
        }
        return count;
    }

    public static void copyToFile(final InputStream source, final File destination) throws IOException {
        final FileOutputStream output = openOutputStream(destination);
        try {
            HIOUtils.copy(source, output);
            output.close(); // don't swallow close Exception if copy completes normally
        } finally {
            HIOUtils.closeQuietly(output);
        }
    }

    public static FileOutputStream openOutputStream(final File file) throws IOException {
        return openOutputStream(file, false);
    }

    public static FileOutputStream openOutputStream(final File file, final boolean append) throws IOException {
        if (file.exists()) {
            if (file.isDirectory()) {
                throw new IOException("文件 '" + file + "' 存在，但是文件是一个目录");
            }
            if (!file.canWrite()) {
                throw new IOException("文件 '" + file + "' 存在，但是文件不能写");
            }
        } else {
            final File parent = file.getParentFile();
            if (parent != null) {
                if (!parent.mkdirs() && !parent.isDirectory()) {
                    throw new IOException("Directory '" + parent + "' could not be created");
                }
            }
        }
        return new FileOutputStream(file, append);
    }

    /**
     * {@link #write(String, String, boolean)}
     *
     * @param data     {@link #write(String, String, boolean)}
     * @param descFile {@link #write(String, String, boolean)}
     * @param append   {@link #write(String, String, boolean)}
     * @throws IOException
     */
    public static void write(byte[] data, String descFile, boolean append) throws IOException {
        write(data, new File(descFile), append);
    }

    /**
     * 写入数据到指定文件
     *
     * @param data     byte[]数组
     * @param descFile 写入到的文件
     * @param append   是否追加文件到文件末尾
     * @throws IOException IO
     */
    public static void write(byte[] data, File descFile, boolean append) throws IOException {
        FileOutputStream fileOutputStream = openOutputStream(descFile, append);
        fileOutputStream.write(data);
        fileOutputStream.flush();
        closeQuietly(fileOutputStream);
    }

    /**
     * 将数据写入到指定的文件中
     *
     * @param content  数据
     * @param descFile 目标文件
     * @param append   是否追加到目标文件后面。
     * @throws IOException
     */
    public static void write(String content, String descFile, boolean append) throws IOException {
        write(content.getBytes(StandardCharsets.UTF_8), descFile, append);
    }


    /**
     * 将数据写入到指定的文件中
     *
     * @param content  数据
     * @param descFile 目标文件
     * @param append   是否追加到目标文件后面。
     * @throws IOException
     */
    public static void write(String content, File descFile, boolean append) throws IOException {
        write(content.getBytes(StandardCharsets.UTF_8), descFile, append);
    }

    public static byte[] read(String path) {
        return HFileUtils.read(path);
    }

    public static byte[] read(Uri uri) {
        String path = HMediaUtils.queryAbsolutePath(uri);
        return HFileUtils.read(path);
    }

    /**
     * 读取文件。如果文件特别大，应该避免将文件读取到内存中（防止内存溢出）。
     *
     * @param file 需要读取的文件
     * @return 返回字节数组
     */
    public static byte[] read(File file) {
        if (file == null) {
            return new byte[0];
        }
        if (file.canRead()) {
            BufferedInputStream bis = null;
            ByteArrayOutputStream bas = new ByteArrayOutputStream();
            FileInputStream in = null;
            try {
                in = new FileInputStream(file);
                bis = new BufferedInputStream(in);
                byte[] buffer = new byte[8 * 1024];
                int c = 0;
                while ((c = bis.read(buffer)) != -1) {
                    bas.write(buffer, 0, c);
                }
                bas.flush();
                return bas.toByteArray();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                HIOUtils.closeQuietly(bas);
                HIOUtils.closeQuietly(bis);
                HIOUtils.closeQuietly(in);
            }
        } else {
            throw new SecurityException("没有读取文件的权限");
        }
        return new byte[0];
    }

    private static OutputStream os;

    public static OutputStream safeOpenOutputStream(String fileName, String mimeType, String dir) throws FileNotFoundException {
        ContentResolver resolver = HLibrary.getAppContext().getContentResolver();
        ContentValues values = new ContentValues();
        values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
        values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
        values.put(MediaStore.MediaColumns.RELATIVE_PATH, dir);
        Uri uri = resolver.insert(MediaStore.Files.getContentUri("external"), values);
        return resolver.openOutputStream(uri, "wt");
    }

    /**
     *
     */
    public static void safeWriteFile(InputStream is, String name, String dir) throws IOException {
        safeOpenWriteFile(name, dir);
        byte[] buffer = new byte[2048];
        int length = 0;
        while ((length = is.read(buffer)) > 0) {
            safeWriteFile(buffer, 0, length);
        }
        closeQuietly(is);
        closeOutputStream();
    }

    /**
     * 安全打开文件流。大文件写入
     * 此方法与
     * safeWriteFile(byte[])
     * closeOutputStream()
     * 顺续使用
     *
     * @param fileName 文件名称，需要带类型
     * @param dir      {link Environment.DIRECTORY_DOWNLOADS，
     *                 DIRECTORY_MUSIC,
     *                 DIRECTORY_PODCASTS,
     *                 DIRECTORY_RINGTONES,
     *                 DIRECTORY_ALARMS,
     *                 DIRECTORY_NOTIFICATIONS,
     *                 DIRECTORY_PICTURES,
     *                 DIRECTORY_MOVIES,
     *                 DIRECTORY_DOWNLOADS,
     *                 DIRECTORY_DCIM,
     *                 DIRECTORY_DOCUMENTS,
     *                 DIRECTORY_AUDIOBOOKS,
     *                 DIRECTORY_RECORDINGS, 等公共文件夹}
     */
    public static void safeOpenWriteFile(String fileName, String dir) {
        try {
            os = safeOpenOutputStream(fileName, MimeTypeUtils.getFileMimeType(fileName), dir);
        } catch (FileNotFoundException e) {
            LogUtils.e(e);
        }
    }

    /**
     * 文件的绝对路径
     *
     * @param fileName 传入文件的绝对路径。
     */
    public static void safeOpenWriteFile(String fileName) {
        try {
            os = openOutputStream(new File(fileName));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 安全打开文件流
     *
     * @param fileName 文件绝对路径
     * @param append   是否追加
     */
    public static void safeOpenWriteFile(String fileName, boolean append) {
        try {
            os = openOutputStream(new File(fileName), append);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static OutputStream getOutputStream() {
        return os;
    }

    /**
     * 写入到文件中
     *
     * @param data
     * @throws IOException
     */
    public static void safeWriteFile(byte[] data, int offset, int length) throws IOException {
        if (os == null) {
            throw new NullPointerException("os 为空，应该先调用safeOpenWriteFile(String)，初始化");
        }
        os.write(data, offset, length);
    }

    /**
     * 关闭流
     */
    public static void closeOutputStream() {
        if (os != null) {

            try {
                os.flush();
                closeQuietly(os);
            } catch (IOException e) {
                LogUtils.e(e);
            } finally {
                os = null;
            }
        }
    }


    /**
     * 直接写入到下载文件夹。写入的文件比较小的可以使用此方法。文件大了会出现OOM异常。
     *
     * @param fileName 文件名
     * @param data     数据
     */
    public static void safeWriteToDownloadFile(String fileName, byte[] data) {
        try {
            OutputStream os = safeOpenOutputStream(fileName, MimeTypeUtils.getFileMimeType(fileName), Environment.DIRECTORY_DOWNLOADS);
            if (os != null) {
                os.write(data);
                os.flush();
                os.close();
            }
        } catch (FileNotFoundException e) {
            LogUtils.e(e);
        } catch (IOException e) {
            LogUtils.e(e);
        }
    }
}
